#ifndef __SOCKETOPT_HPP__
#define __SOCKETOPT_HPP__

#include <string>

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>

class SocketOpt {
public:
    // 全连接队列长度
    static const int global_backlog=10;

    // 获取字节流或者数据包套接字
    static int SocketCreate(int type);

    // 绑定端口
    static void SocketBind(int sockfd,int port);

    // 设置监听状态
    static void SocketSetListen(int sockfd,int backlog=global_backlog);

    // 申请连接
    static bool SocketConnect(int sockfd,const std::string &server_ip,const uint16_t &server_port);

    // 获取连接
    static int SocketAccept(int listensock,std::string *client_ip,uint16_t *client_port);

    // 对已经存在的文件描述符设置非阻塞
    static bool SetNonBlock(int fd);
};

int SocketOpt::SocketCreate(int type) {
    
    if(type!=SOCK_STREAM || type!=SOCK_DGRAM) {
        return -1;
    }

    // int socket(int domain, int type, int protocol);
    int sockfd=socket(AF_INET,type,0);

    if(-1==sockfd) {
        perror("socket");
        exit(1);
    }

    // 防止服务端出现TIME_WAIT状态不能及时绑定端口
    if(SOCK_STREAM==type) { 
        // int setsockopt(int sockfd, int level, int optname,
        //    const void *optval, socklen_t optlen); 
        int opt = 1;
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));       
    }

    return sockfd;
}

void SocketOpt::SocketBind(int sockfd,int port) {

    struct sockaddr_in local;
    memset(&local,0,sizeof(local));

    local.sin_family=AF_INET;
    local.sin_port=htons(port);
    local.sin_addr.s_addr=INADDR_ANY;

    socklen_t len=sizeof(local);
    // int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    if(-1==bind(sockfd,(struct sockaddr *)&local,len)) {
        perror("bind");
        exit(2);
    }
}

void SocketOpt::SocketSetListen(int sockfd,int backlog) {

    // int listen(int sockfd, int backlog);
    if(-1==listen(sockfd,backlog)) {
        perror("listen");
        exit(3);
    }

}

void SocketOpt::SocketConnect(int sockfd,const std::string &server_ip,const uint16_t &server_port) {

    struct sockaddr_in peer;
    bzero(&peer,sizeof(peer));
    
    peer.sin_family=AF_INET;
    peer.sin_addr.s_addr=inet_addr(server_ip.c_str());
    peer.sin_port=htons(server_port);

    // int connect(int sockfd, const struct sockaddr *addr,
    //              socklen_t addrlen);
    if(-1==connect(sockfd,(struct sockaddr *)&peer,sizeof(peer))) {
        perror("connect");

        return false;
        //exit(4);
    }

    return true;
}

int SocketOpt::SocketAccept(int listensock,std::string *client_ip,uint16_t *client_port) {

    struct sockaddr_in peer;
    socklen_t socklen=sizeof(peer);
    memset(&peer,0,socklen);

    // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    int sockfd=accept(listensock,(struct sockaddr *)&peer,&socklen);

    if(-1==sockfd) {
        perror("accept")
        return -1;
    }

    if(client_ip!=nullptr) {
        *client_ip=std::string(inet_ntoa(peer.sin_addr));
    }    

    if(client_port!=nullptr) {
        *client_ip=ntohs(peer.sin_port);
    }

    return sockfd;
}

bool SocketOpt::SetNonBlock(int fd) {

    // 1.先获取当前文件描述符的状态
    int fl=fcntl(fd,F_GETFL);

    if(-1==fl) {
        return false;
    }

    // 2.设置非阻塞
    int ret=fcntl(fd,F_SETFL,fl | O_NONBLOCK);

    return ret!=-1;
}

#endif
